#include "libavdecode.h"
#include "stream/streambase.h"
#include "log/EZQLOG.h"
#include <QMutex>
#include <QMetaType>
#include <QFile>


int c_contextReadPacket(void *opaque, uint8_t *buf, int buf_size)
{
    LibavDecode *decode = (LibavDecode*)opaque;
    int size = decode->contextReadPacket(buf,buf_size);
    return size;
}
int64_t c_decodeSeek(void *opaque, int64_t offset, int whence)
{
    LibavDecode *decode = (LibavDecode*)opaque;
    return decode ->decodeSeek(offset,whence);
}


static void ffmpegInit()
{
    static bool isInit = false;
    if(isInit==false){
        avcodec_register_all();
        av_register_all();
        isInit = true;
        qRegisterMetaType<LibavStatus>();
    }
}

LibavDecode::LibavDecode(int pcmBufSize)
    :QObject(0)
{
    ffmpegInit();
    _stream = NULL;
    _buffBlockSize = 1024*32;//32K
    setDecodeStatus(LibavStatus(LibavDecodeStatus_init,0));
//    _decodeThread = NULL;
    _codeContext = 0;
    _buf = 0;
    _formatContext = 0;
    _ioContext = 0;
    _pcmBuf = new unsigned char[pcmBufSize];
    _pcmBufSize = pcmBufSize;
    _pcmBufLenOil = 0;
//    _isStop = false;
    _operationIndex = 0;
    _seekPos = 0;
}
LibavAutioFormat LibavDecode::audioFormat() const
{
    return _audioFormat;
}

void LibavDecode::setAudioFormat(const LibavAutioFormat &audioFormat)
{
    _audioFormat = audioFormat;
}
void LibavDecode::setSeekPos(const qint64 &pos)
{
//    EZDebug << "setSeekPos 1";
    stopDecode();
//    EZDebug << "setSeekPos 2";
    _seekPos = pos;
    decode(false);
}

qint64 LibavDecode::duration() const
{
    return _duration;
}

void LibavDecode::setDuration(const qint64 &duration)
{
    _duration = duration;
}



LibavDecode::~LibavDecode()
{
    stopDecode();
    setStream(0);
    clearProbe();
    delete _pcmBuf;

}
StreamBase *LibavDecode::stream() const
{
    return _stream;
}

void LibavDecode::setStream(StreamBase *stream)
{
    makeoperationIndex();
    QMutexLocker loker(&_streamLock);
    clearProbe();
    _seekPos = 0;
    _probeFinish = false;
    if(_stream){
        disconnect(_stream,SIGNAL(availableChange(qint64,qint64)),this,SLOT(onStreamAvailableChange(qint64,qint64)));
        _stream->Release();
    }
    if(stream){
        stream->Retain();
        connect(stream,SIGNAL(availableChange(qint64,qint64)),this,SLOT(onStreamAvailableChange(qint64,qint64)));
    }
    _stream = stream;
    _duration = -1;
//    _streamOil = 0;

    _streamWaitCondition.wakeAll();

}
int LibavDecode::buffBlockSize() const
{
    return _buffBlockSize;
}

void LibavDecode::setBuffBlockSize(int buffBlockSize)
{
    _buffBlockSize = buffBlockSize;
}




void LibavDecode::onStreamAvailableChange(qint64 /*offset*/, qint64 /*dataLen*/)
{
    QMutexLocker loker(&_streamLock);
    _streamWaitCondition.wakeAll();
}

int LibavDecode::contextReadPacket(uint8_t *buf, int buf_size)
{

    qint64 hasLen = 0;//_stream->validFrom(_streamOil);
    bool isRun = true;
    _streamLock.lock();
    qint64 pos = _ioContext->pos;

//    EZDebug << "contextReadPacket" << pos;
    while (_stream && isRun && _curDecodeOperationIndex == _operationIndex) {
        hasLen = _stream->validFrom(pos);
        if(hasLen < buf_size && _stream->isEof()==0){
            if(_decodeStatus.parentStatus == LibavDecodeStatus_probe &&
               _decodeStatus.subStatus != LibavDecodeStatus_probe_ing){
                setDecodeStatus(LibavStatus(LibavDecodeStatus_probe,LibavDecodeStatus_probe_ing));
            }
            _streamWaitCondition.wait(&_streamLock);
        }else{
            break;
        }
        if(_curDecodeOperationIndex != _operationIndex){
            isRun = false;
        }

    }
    int realSize = 0;
    if(isRun && _curDecodeOperationIndex == _operationIndex){
        if(hasLen > 0){
            int readSize = buf_size > hasLen ? hasLen:buf_size;
            realSize = _stream->read(buf,pos,readSize);
//            _streamOil += realSize;
        }else{
            if(_stream->isEof()==-1){
                realSize = -10086;
            }else if(_stream->isEof()==1){
                realSize = -10088;
            }
        }
    }
    _streamLock.unlock();
    return realSize;
}
int64_t LibavDecode::decodeSeek(int64_t offset, int whence)
{

    int64_t value = 0;
    _streamLock.lock();
    if(whence==AVSEEK_SIZE){
        value = _stream->size() - offset;
//        _streamOil = offset;
    }else if(whence==AVSEEK_FORCE){
        value = offset;
    }else if(whence==0){
        value =  offset;
    }else{
        value = 0;
        EZDebug << "decode unknow whence " << whence;
    }
//    _streamOil = value;
//    EZDebug << "decodeSeek:" << value << whence << offset << "";
    _streamLock.unlock();
    return value;
}

int LibavDecode::decode(bool onlyProbe)
{
    if(_stream==0){
        return -1;
    }

    this->Retain();
    int index = makeoperationIndex();
    _willDecode = !onlyProbe;
    EZQThread *decodeThread = new EZQThread(this,(EZQThreadFunc0)&LibavDecode::decodeInThread);
    decodeThread->setUserData("operationIndex",QVariant(index));
    connect(decodeThread,SIGNAL(finished()),this,SLOT(decodeInThreadFinish()));
    decodeThread->start();

    return 0;
}
void LibavDecode::stopDecode()
{
//    _isStop = true;
    makeoperationIndex();

    _pcmBufLock.lock();
    _pcmBufWaitCondition.wakeAll();
    memset(_pcmBuf,0,_pcmBufSize);
    _pcmBufLenOil = 0;
    _pcmBufLock.unlock();

    _streamLock.lock();
    _streamWaitCondition.wakeAll();
    _streamLock.unlock();

    _decodeMutex.lock();
//    setDecodeStatus(LibavStatus(LibavDecodeStatus_init,0));
    _pcmBufLenOil = 0;
    _decodeMutex.unlock();

}

int LibavDecode::probeInfo()
{
    return decode(true);
}
void LibavDecode::clearProbe()
{
    if(_codeContext){
        avcodec_flush_buffers(_codeContext);
        _codeContext = 0;//这个应该不必free
    }

//    if(_buf){
//        av_free(_buf);
//        _buf = 0;
//    }
    if(_ioContext){
        av_free(_ioContext);
        _ioContext = 0;
    }
    if(_formatContext){
//        avformat_close_input(&_formatContext);
        avformat_free_context(_formatContext);
        _formatContext = 0;
    }
}
void LibavDecode::decodeInThreadFinish()
{
    EZQThread *thread = (EZQThread*)sender();
    thread->Release();
    this->Release();
}
void LibavDecode::decodeInThread(EZQThread *thread)
{
    int operationIndex = thread->getUserData("operationIndex").toInt();
    _decodeMutex.lock();
    if(operationIndex != _operationIndex){
        _decodeMutex.unlock();
        return;
    }
    _curDecodeOperationIndex = operationIndex;
    if(_probeFinish==false){
        decode_probe();
    }
    if(_curDecodeOperationIndex==_operationIndex &&
            _willDecode && _probeFinish
            ){
        decode_decode();
    }
    _decodeMutex.unlock();
}

void LibavDecode::writePcmBuf(unsigned char *pcm, int size)
{
//    EZDebug << "writePcmBuf enter: " << size << _pcmBufLenOil;
//    QThread *thread = QThread::currentThread();
//    EZDebug << "writePcmBuf:" << thread;
    _pcmBufLock.lock();
    int writeSize = 0;
    do{
        if(_curDecodeOperationIndex!=_operationIndex){
            break;
        }
        int spareSize = _pcmBufSize - _pcmBufLenOil;
        int willWriteSize = spareSize>size-writeSize ? size-writeSize:spareSize;
        memcpy(_pcmBuf+_pcmBufLenOil,pcm+writeSize,willWriteSize);
        writeSize += willWriteSize;
        _pcmBufLenOil += willWriteSize;
        if(writeSize < size){
            //todo sync call signal
            emit sidecodePcmAvailableChange();
//            EZDebug << "writePcmBuf emit and wait";
            _pcmBufWaitCondition.wait(&_pcmBufLock);
//            EZDebug << "writePcmBuf has run";
        }else{
            break;
        }
    }while(true);
    _pcmBufLock.unlock();
}

int LibavDecode::pcmBufSize()
{
    QMutexLocker loker(&_pcmBufLock);
    return _pcmBufLenOil;
}

int LibavDecode::readPcmBuf(unsigned char *buffer, int bufferSize)
{

    int readLen = 0;
    _pcmBufLock.lock();

    readLen = bufferSize > _pcmBufLenOil? _pcmBufLenOil:bufferSize;
    memcpy(buffer,_pcmBuf,readLen);


    memmove(_pcmBuf, _pcmBuf+readLen, _pcmBufLenOil-readLen);
    _pcmBufLenOil -= readLen;
//    int spareSize = _pcmBufSize - _pcmBufLenOil;
//    EZDebug << "readPcmBuf readLen"<< readLen << "spareSize" << spareSize;
    _pcmBufWaitCondition.wakeAll();
    _pcmBufLock.unlock();
    return readLen;
}

void LibavDecode::decode_decode()
{
    setDecodeStatus(LibavStatus(LibavDecodeStatus_decode,LibavDecodeStatus_decode_start));
    setDecodeStatus(LibavStatus(LibavDecodeStatus_decode,LibavDecodeStatus_decodes_ing));

    int error = 0;

//    avcodec_flush_buffers(_codeContext);
    error = av_seek_frame(_formatContext, -1, _seekPos, AVSEEK_FLAG_BACKWARD);
    avcodec_flush_buffers(_codeContext);
    if(error < 0){
        EZDebug << "av_seek_frame error:" << error;
    }
//    av_read_pause(_formatContext);
    av_read_play(_formatContext);

    AVPacket pkt;
    av_init_packet(&pkt);
    AVFrame *frame= av_frame_alloc();
    int framePtr;

    int runError = 0;
//    EZDebug << "_ioContext->pos" << _ioContext->pos;
    do{
        error = av_read_frame(_formatContext,&pkt);
//        EZDebug << pkt.pos << pkt.pts << pkt.dts;
        if(error>=0 ){
            if( pkt.stream_index==_audioIndex){

                int srcCount = pkt.size;
                while (srcCount > 0 && _curDecodeOperationIndex == _operationIndex) {
                    int decodeCount = avcodec_decode_audio4(_codeContext,frame,&framePtr,&pkt);
                    if (decodeCount >= 0) {
                        //
                        int in_sample = frame->nb_samples;
                        if(_codeContext->sample_fmt >= AV_SAMPLE_FMT_U8P) //planar format
                        {
                            int channels = _audioFormat.channels;
                            int size ;//= in_sample * _audioFormat.channels * (_audioFormat.sampleSize/8);
                            size = frame->linesize[0] * channels;

                            unsigned char *tempBuf = new unsigned char[size];
                            memset(tempBuf,0,size);
                            int bitSample = _audioFormat.sampleSize/8;
                            for(int i=0;i<in_sample;i++){
                                for(int c=0;c<channels;c++){

                                    memcpy(tempBuf+i*bitSample*channels+c*bitSample,
                                           frame->extended_data[c]+i*bitSample,
                                           bitSample);
                                }
                            }

                            writePcmBuf(tempBuf,size);
                            delete tempBuf;

                        }else{
//                            if(frame->extended_data[1]==0){
//                                EZDebug << "adfsdf";
//                            }
                            int size ;//= in_sample * _audioFormat.channels * (_audioFormat.sampleSize/8);
                            size = frame->linesize[0];
                            writePcmBuf(frame->extended_data[0], size);
                        }
                        srcCount -= decodeCount;
                    }else{
                        EZDebug << "avcodec_decode_audio4 error:%d" << decodeCount;
                        srcCount = -1;
                    }


                }


            }
            av_free_packet(&pkt);
        }else{
            EZDebug << "av_read_frame error:%d" <<error;
//            isRun = false;
            if(error==-10086){
                runError = -2;
            }else if(error==-10088){
                runError = -3;
            }else{

                runError = -2;
            }
        }
        if(_curDecodeOperationIndex != _operationIndex){
            runError = -1;
        }
    }while(runError==0);
    av_read_pause(_formatContext);

    setDecodeStatus(LibavStatus(LibavDecodeStatus_decode,LibavDecodeStatus_decodes_stop));

    if(runError==-1){ // cancel
        setDecodeStatus(LibavStatus(LibavDecodeStatus_cancel,0));
    }else if(runError==-2){ // error
        setDecodeStatus(LibavStatus(LibavDecodeStatus_fail,0));
    }else if(runError==-3){ // finish
        setDecodeStatus(LibavStatus(LibavDecodeStatus_finish,0));
    }
}

void LibavDecode::decode_probe()
{
//    QThread *thread = QThread::currentThread();

    setDecodeStatus(LibavStatus(LibavDecodeStatus_probe,LibavDecodeStatus_probe_start));
    _audioIndex = -1;
    clearProbe();
    _buf = (uint8_t*)av_mallocz(sizeof(uint8_t)*_buffBlockSize);
    _ioContext = avio_alloc_context(_buf,_buffBlockSize,0,this,c_contextReadPacket,0,c_decodeSeek);
    _ioContext->seekable = 0;
    _formatContext = avformat_alloc_context();
    _formatContext->pb = _ioContext;
    AVInputFormat *format = 0;

    int error = av_probe_input_buffer(_ioContext,&format,"",0,0,_stream->size());
    if(error<0){
        setDecodeStatus(LibavStatus(LibavDecodeStatus_fail,0));
        return;
    }

    AVFormatContext *pFmt = _formatContext;
    error = avformat_open_input(&pFmt,"",format,NULL);
    if(error<0){
        setDecodeStatus(LibavStatus(LibavDecodeStatus_fail,0));
        return;
    }

    error = avformat_find_stream_info(pFmt,NULL);
    if(error<0){
        setDecodeStatus(LibavStatus(LibavDecodeStatus_fail,0));
        return;
    }
    av_dump_format(pFmt, 0, "", 0);
    int audioindex = -1;
    for ( int i = 0; i < (int)pFmt->nb_streams; i++) {
        if ( pFmt->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO ) {
            audioindex = i;
            break;
        }
    }

    if(audioindex < 0){
        error = -1;
        setDecodeStatus(LibavStatus(LibavDecodeStatus_fail,0));
        return;
    }
    _audioIndex = audioindex;
    AVStream *audioStrem = pFmt->streams[audioindex];
    _codeContext = audioStrem->codec;

    AVCodec *pAudioCodec = NULL;
    pAudioCodec = avcodec_find_decoder(_codeContext->codec_id);

    error = avcodec_open2(_codeContext,pAudioCodec,NULL);
    if(error<0){
        setDecodeStatus(LibavStatus(LibavDecodeStatus_fail,0));
        return ;
    }
    if(_codeContext->channels <=0 || _codeContext->sample_rate<=0 ){
        setDecodeStatus(LibavStatus(LibavDecodeStatus_fail,0));
        return;
    }
    av_read_pause(_formatContext);

    _probeFinish = true;
    _duration = pFmt->duration ;
    LibavAutioFormat af;
    af.channels = _codeContext->channels;
    af.sample_rate = _codeContext->sample_rate;
    af.bitRate = pFmt->bit_rate;
//    pFmt->duration
    switch(_codeContext->sample_fmt){
    case AV_SAMPLE_FMT_U8:
        af.sampleSize = 8;
        break;
    case AV_SAMPLE_FMT_S16:
        af.sampleSize = 16;
        break;
    case AV_SAMPLE_FMT_S32:
        af.sampleSize = 32;
        break;
    case AV_SAMPLE_FMT_FLT:
        af.sampleSize = 32;
        break;
    case AV_SAMPLE_FMT_DBL:
        af.sampleSize = 32;
        break;
    case AV_SAMPLE_FMT_U8P:
        af.sampleSize = 8;
        break;
    case AV_SAMPLE_FMT_S16P:
        af.sampleSize = 16;
        break;
    case AV_SAMPLE_FMT_S32P:
        af.sampleSize = 32;
        break;
    case AV_SAMPLE_FMT_FLTP:
        af.sampleSize = 32;
        break;
    case AV_SAMPLE_FMT_DBLP:
        af.sampleSize = 32;
        break;
    default:
        EZDebug << "af.sampleSize erro _codeContext->sample_fmt is" << _codeContext->sample_fmt;
        break;
    }
    setAudioFormat(af);


    setDecodeStatus(LibavStatus(LibavDecodeStatus_probe,LibavDecodeStatus_probe_finish));

}

LibavStatus LibavDecode::decodeStatus() const
{
    return _decodeStatus;
}

void LibavDecode::setDecodeStatus(const LibavStatus &decodeStatus)
{
    _decodeStatus = decodeStatus;
    emit sidecodeStateChange(decodeStatus);
//    EZDebug << _decodeStatus.parentStatus << _decodeStatus.subStatus;
}

int LibavDecode::makeoperationIndex()
{
    int currentIndex = 0;
    _operationIndexMutex.lock();
    _operationIndex = (_operationIndex+1)%10000;
    currentIndex = _operationIndex;
    _operationIndexMutex.unlock();
    return currentIndex;
}





